/**
 * File: FilterGraphByBpCoverage.java
 * Created by: mhaimel
 * Created on: Feb 3, 2011
 * CVS:  $Id: FilterGraphByBpCoverage.java 1.0 Feb 3, 2011 3:54:28 PM mhaimel Exp $
 */
package uk.ac.ebi.curtain.controller.graph;

import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;

import uk.ac.ebi.curtain.controller.SingleSubmitController;
import uk.ac.ebi.curtain.model.graph.Arc;
import uk.ac.ebi.curtain.model.graph.Graph;
import uk.ac.ebi.curtain.model.graph.Node;
import uk.ac.ebi.curtain.model.graph.NodeEdge;
import uk.ac.ebi.curtain.model.graph.curtain.ContigInfo;
import uk.ac.ebi.curtain.model.graph.curtain.CurtainGraph;
import uk.ac.ebi.curtain.model.graph.curtain.ReadWrapper;
import uk.ac.ebi.curtain.model.graph.curtain.filter.NodeCoverageCombinedFilter;

/**
 * @author mhaimel
 *
 */
public class FilterGraphByBpCoverage  extends SingleSubmitController<Serializable, Serializable> {

	@Override
	protected void singleSubmit(List<Serializable> inputList) {
		CurtainGraph curtainGraph = getContext().getGraph();
		CurtainGraph filtered = analyse(curtainGraph);
		getContext().setGraph(filtered);
		for(Serializable s : inputList){
			submit(s);
		}
	}

	public CurtainGraph analyse(CurtainGraph curtainGraph) {
		Graph<ContigInfo, ReadWrapper> graph = curtainGraph.getGraph();
		int minCov = getContext().getArguments().getFilterCovMin().get();
		int maxCov = getContext().getArguments().getFilterCovMax().get();
		if(maxCov <= minCov){
			if(maxCov == -1 && minCov == 0){
				throw new IllegalArgumentException("Please specify Minimum and Maximum Coverage filter parameters!!!");
			}
			if(maxCov != -1){
				throw new IllegalArgumentException("Please provide valid Coverage paramater: min " + minCov+ " >= max " + maxCov );
			}
		}
		Integer graphCat = curtainGraph.getCategories();
		int[]arr = new int[graphCat];
		for(int i = 0; i < graphCat; ++i){
			arr[i] = i;
		}
		// reindex before starting
		graph.buildArcsIndex();
		graph.reindexArcs();

		getLog().debug("Current Node info: " + curtainGraph.graphNodeSummary());
		getLog().info("Filter Contigs of combined sequence Coverage ... ");
		getLog().debug("Coverage cutoffs: " + minCov + " min; " + maxCov + " max;");
		NodeCoverageCombinedFilter fNodes = graph.processNodes(new NodeCoverageCombinedFilter(minCov,maxCov,arr));

		printStats(fNodes.getRemoved(), "Removed");
		printStats(fNodes.getValid(), "Valid");	
		
		deleteArcs(graph,fNodes.getRemoved());
		
		CurtainGraph filtered = copyValidNodes(curtainGraph,fNodes.getValid());
		// reindex before starting
		filtered .getGraph().buildArcsIndex();
		filtered.getGraph().reindexArcs();
		return filtered;
	}


	private CurtainGraph copyValidNodes(CurtainGraph curtainGraph,LinkedBlockingQueue<Node<ContigInfo, ReadWrapper>> nodes) {
		Map<Long, Long> old2new = new HashMap<Long, Long>();
		
		CurtainGraph ncg = new CurtainGraph(curtainGraph.getCategories(), (long) nodes.size());
		Graph<ContigInfo, ReadWrapper> g = ncg.getGraph();
		Long id = 0l;
		for(Node<ContigInfo, ReadWrapper> n : nodes){
			Node<ContigInfo, ReadWrapper> nNode = ncg.getEmptyNode(id, n.getValue().getLength());
			nNode.setValue(n.getValue());
			
			nNode.setActive(n.getActive().get());
			nNode.setStatus(n.getStatus().get());			
			old2new.put(n.getId(), id);
			++id;
		}
		

		for(Node<ContigInfo, ReadWrapper> n : nodes){
			for(NodeEdge ne : NodeEdge.values()){
				List<Arc<ContigInfo, ReadWrapper>> al = n.getArcs(ne);
				for(Arc<ContigInfo, ReadWrapper> a : al	){
					a.detachFromNodes();
					
					Node<ContigInfo, ReadWrapper> left = 
						ncg.getNode(
								old2new.get(
										a.getLeft().getId()));
					Node<ContigInfo, ReadWrapper> right = 
						ncg.getNode(
								old2new.get(
										a.getRight().getId()));;
					NodeEdge leftEdge = a.onLeftNode();
					NodeEdge rightEdge = a.onRightNode();
					Arc<ContigInfo, ReadWrapper> na = g.createArc(left, right, leftEdge, rightEdge);
					na.setValue(a.getValue());
				}
			}
			
		}
		g.buildArcsIndex();
		g.reindexArcs();
		return ncg;
	}

	private void deleteArcs(Graph<ContigInfo, ReadWrapper> graph, LinkedBlockingQueue<Node<ContigInfo, ReadWrapper>> nodes) {
		for(Node<ContigInfo, ReadWrapper> n : nodes){
			for(NodeEdge ne : NodeEdge.values()){
				List<Arc<ContigInfo, ReadWrapper>> al = n.getArcs(ne);
				for(Arc<ContigInfo, ReadWrapper> a : al	){
					graph.removeArc(a);
				}
			}
		}
	}

	private void printStats(Collection<Node<ContigInfo, ReadWrapper>> nodes, String type) {
		long total = 0;
		for(Node<ContigInfo, ReadWrapper> n : nodes){
			total += n.getValue().getLength();
		}
		getLog().info(type+" total length: " + total+"; Count: " + nodes.size());
	}
}









